亚洲中文字幕无码AV,九九天天干人妻天天操天天玩,色欲久久国产色AV免费看老司,国产熟妇另类久久久久

【20年品牌建站】找北京網站建設公司就選新鴻儒/提供北京網站建設報價/北京網站制作/北京網站設計/網站開發、北京網站建設公司電話【400-024-1998】有優惠哦!
簡體
繁體 簡體
我們的服務遍布中國

我們的服務遍布中國
乃至世界

新鴻儒所服務的品牌地域與城市
北京 天津 上海 廣州 深圳 香港 廈門 江蘇 浙江 山東
重慶 長沙 武漢 成都 西安 寧夏 麗江 青海 云南 烏魯木齊
黑龍江 內蒙古 河北 ...
新鴻儒服務與合作的全球各地
美國 加拿大 德國 法國 英國 瑞士 意大利 荷蘭
印度 日本 韓國 ...

不論你的品牌在何處
我們都可以提供完善的服務與幫助

致電

400-024-1998

Godaddy服務器上關于ASP.NET網站建設一些經驗 - 斷點續傳下載 (二)

發布時間:2013-11-22 瀏覽:377打印字號:

 談談在APS.NET中如何控制文件下載.

設計目的和要求

假設這么一個應用場景:
一個主機,上面存有許多文件資料,有各種文件格式.(PDF, DOC, EXE ... 等等).
該主機上運行一個ASP.NET網站, 用戶注冊,并付費之后允許他/她下載資料.

文件是放在IIS服務器上的, 如果用戶知道具體路徑那么他是可以隨時下載的. (在沒有或者不能設置訪問權限的情況下.)
如果直接把下載路徑發送給付費用戶,肯定是行不通的,會被散播出去. 所以不能把讓客戶端得知具體路徑,文件內容由 ASP.NET 服務器頁面讀取后發送給客戶端. 
 
要做的就是: 編寫一個ASP.NET 頁面服務器代碼, 讀取指定文件,并發送給客戶 .

總體思路

.net 里, 有2個函數可以用來發送文件 Response.WriteFile 和 Response.TransmiteFile
它們的主要區別是: WriteFile 是先把文件內容讀取到服務器緩沖,然后再發送到客戶端. 所以對于大文件,會造成服務器很大的壓力.
一般用來處理小文件,比如,發送給 excel 報表之類的. TransmiteFile 不緩沖數據, 直接拋給客戶端, 所以可以用來發大文件.
( 采用 TransmiteFile 來實現.)

具體實現

1. 給客戶一個鏈接,形如 http://xxxx/downloads.aspx?Key=ABCD123456

2. 在downloads.aspx的服務器代碼中, 通過Key的值,查詢數據庫,得到服務器上的真實文件路徑. 這個時候,控制權在 downloads.aspx, 所以可以編寫復雜的控制功能, 比如看看用戶有沒有登錄,有沒有付費之類的,從而避免外部盜鏈.

3. 得到文件路徑后,調用 Response.TransmiteFile 發送文件給客戶端.

4. 因為給客戶的鏈接里沒有任何文件名的信息, 所以要在HTTP響應頭里添加一句,告訴客戶端文件名:  Response.AddHeader("Content-Disposition", "attachment; filename=/"" + 你的文件名 + "/""); (如果要支持中文,要考慮編碼的問題, 我這里不說,不是我們的主題.)

5. 如果是一個大文件, 比如1G, 不支持斷點續傳,是沒有意義的. 那么如何實現呢?

(1) 要讓客戶端知道我們的服務器支持斷點續傳, 要在HTTP響應頭中包含 Accept-Ranges: bytes 和 ETag: "XXXX".
 ETag 是一個文件的標識, 供客戶端判斷它請求的是同一個文件, ETag 的內容在HTTP規范里并沒有具體要求,只要保證在同一個服務器上,同一個文件有相同的ETag 就行了, 一般就根據文件名和最后修改時間生成一個字符串就可以了.
 
代碼示例:
Response.AddHeader("Accept-Ranges", "bytes");  // 斷點續傳控制.
Response.AddHeader("ETag", "/"" + strETag + "/""); // 允許斷點續傳


(2) 要處理客戶端請求中的 "Range" 字段. 一般格式是這樣: Range: bytes=1234- 或者 Range: bytes=1234-12345
分別表示從地1235個字節開始下載和下載第1235到第12346個字節之間的數據.
服務器首先要添加 Content-Range 響應頭, 然后用 TransmiteFile 發送指定的數據.

代碼示例:
Response.StatusCode = 206;
Response.AddHeader("Content-Length", (lTo - lFrom + 1).ToString());
Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", lFrom, lTo, fi.Length));  // 參數0 和 參數1 是位置. 參數2是文件長度
Response.TransmitFile(strFilePath, lFrom, lTo - lFrom + 1);

( 其中, lFrom 和 lTo 是根據客戶端請求中的 Range 字段得到的.)
 

說一下優缺點:

1. 可以隨心所欲的控制下載.
2. 可以繞過服務器文件類型下載的限制, 比如服務器不允許下載 ISO 和 NRG 文件擴展名的文件, 如果直接輸入RUL會提示404, 但是用上述的方法可以下載.

3.用這種辦法的話,下載是在.net的一個線程里做的,如果用戶量大的話,需要維護多個響應

附注:

1. TransmitFile(String) ( 函數是 .net 2.0 才加上去的.

2. TransmitFile(String, Int64, Int64) 帶發送位置參數的重載是 .net 2.0 sp1 以后才支持的. 所以要用本文所說的方法實現斷點續傳, 至少要支持.net 2.0 sp1

3. 沒有檢測請求頭中的 If-Range 和 Unless-Modified-Since, 如果有需要,在得到文件名之后就可以校驗一下, 分別對應 ETag 和 Last-Modified.


              // 1. 獲取服務器上的文件路徑 // 這里,如果文件路徑有問題, 無法映射則會拋出異常, strURL 是根據 Key從數據庫中查詢到的真實文件路徑
                  string strFilePath = Server.MapPath("~" + strURL);
                 
                  // 2. 獲取文件名
                  string strFileName = System.IO.Path.GetFileName(strFilePath);

                  // 3. 確認文件是否存在
                  FileInfo fi = new FileInfo(strFilePath);
                  if (!fi.Exists)
                  {
                      // 退出點,文件不存在
                  }

                  // 4. 拋給客戶端
                  strFileName.Replace(" ", "%20"); // 處理文件名含空格的情況
                  string strETag = strFileName.ToUpper() + ":" + fi.Length.ToString();  // 我的Etag 是用文件名和字節數構成,馬馬虎虎湊合用.
                  string strLastTime = fi.LastWriteTimeUtc.ToString("r");

                  Response.Clear();  // 先把響應流清空
                  Response.ContentType = "application/octet-stream";  // 指定文件類型,使客戶端總是彈出保存文件的框框.
                  Response.AddHeader("Content-Disposition", "attachment; filename=/"" + strFileName + "/"");
                  Response.AddHeader("Accept-Ranges", "bytes");  // 斷點續傳控制.
                  Response.AddHeader("ETag", "/"" + strETag + "/""); // 允許斷點續傳
                  Response.AddHeader("Last-Modified", strLastTime);//把最后修改日期寫入響應

                  // 獲取客戶端請求的范圍, 并且要校驗這個范圍的有效性
                  long lFrom = 0;
                  long lTo = 0;
                  bool bParts = false;
                  string strRange = Request.Headers["Range"];
                  if (ParseRange(strRange, out lFrom, out lTo))  /// ParseRange 是我自己寫的函數, 從 Range 中讀取2個位置.代碼在后面.
                  {
                      if (-1 == lFrom && -1 == lTo)
                      {
                          // 不允許2個值都不指定
                      }
                      else
                      {
                          if (lTo == -1) lTo = fi.Length - 1;  // 客戶端未指定結束位置,則認為是文件的最后一個字符 Range: bytes=123- 的情況
                          if (lFrom == -1) // Range: bytes=-123 的情況, 請求最后的123個字節
                          {
                              lFrom = fi.Length - lTo;
                              lTo = fi.Length - 1;
                          }

                          if (lFrom < 0 || lFrom >= fi.Length || lFrom > lTo || lTo < 0 || lTo >= fi.Length)
                          {
                              // 以上幾種情況下,范圍的值能解析出來,但是不合法.
                              // 首先 From 和 To 的下標都應該在文件長度范圍內
                              // 其次 From 應該 <= To
                          }
                          else
                          {
                              bParts = true;
                          }
                      }
                  }

                  // 根據用戶請求,返回數據段或者整個文件
                  if(bParts)
                  {
                      Response.StatusCode = 206;
                      Response.AddHeader("Content-Length", (lTo - lFrom + 1).ToString());
                      Response.AddHeader("Content-Range", string.Format("bytes {0}-{1}/{2}", lFrom, lTo, fi.Length));  // 參數0 和 參數1 是位置,從0開始. 參數2是文件長度
                      Response.TransmitFile(strFilePath, lFrom, lTo - lFrom + 1);
                  }
                  else
                  {
                      Response.AddHeader("Content-Length", fi.Length.ToString());
                      Response.TransmitFile(strFilePath);
                  }
                  Response.End();
              }


=============================傳說中的分割線======================================
protected bool ParseRange(string strRange, out long lFrom, out long lTo)
    {
        lFrom = 0;
        lTo = 0;
        long lTemp = 0;
        if (strRange == null || strRange == "")
        {
            return false; // 字符串為空
        }
        else
        {
            strRange = strRange.Replace(" ", ""); // 去除多余的空格
            string[] range = strRange.Split(new char[] { '=', '-' });

            // 1.分割后,包含3段 第一段是 "Range: bytes", 第二段是起始位置, 第三段是結束位置
            if (range.Length != 3)
            {
                return false; // 格式不正確 只支持 Range: bytes=89294317- 或者 Range: bytes=1234-1235 或者 Range: bytes=-500 3種格式.
            }

            // 2. 解析起始位置
            if (range[1].Length <= 0)
            {
                // 起始位置未指定
                lFrom = -1;
            }
            else
            {
                if (!long.TryParse(range[1], out lTemp))
                {
                    return false; // 起始位置無法解析
                }
                lFrom = lTemp;
            }

            // 3. 解析結束位置
            if (range[2].Length <= 0)
            {
                lTo = -1; // 沒有指定結束位置 Range: bytes=1234- 的情況
            }
            else
            {
                if (!long.TryParse(range[2], out lTemp))  // 排除 byte=xxxx- 的情況 TryParse 失敗, 會把lTemp 置零
                {
                    return false; // 第三度的內容不為空,但是無法解析
                }
                lTo = lTemp;
            }
            return true;
        }
    }

現在就與新鴻儒客服交流

400-024-1998

您也可進行在線咨詢或預約項目顧問
我要預約
在線咨詢